﻿#include "../../src/box/box_channel.hh"
#include "../../src/box/box_network.hh"
#include "../../src/detail/box_config_impl.hh"
#include "../../src/util/time_util.hh"
#include "../../thirdparty/knet/knet/ringbuffer.h"
#include "../framework/unittest.hh"
#include <initializer_list>
#include <thread>

FIXTURE_BEGIN(test_box_network)

class TestNetwork : public kratos::service::BoxNetwork {
public:
  bool listened{false};
  bool accepted{false};
  bool connected{false};
  bool closed{false};
  bool recved{false};

  kratos::config::BoxConfigImpl config_;
  std::shared_ptr<kratos::service::BoxChannel> connector;
  std::shared_ptr<kratos::service::BoxChannel> client;
  std::string recv_string;

  TestNetwork() : config_(nullptr) {}
  virtual ~TestNetwork() {}
  virtual auto get_config() -> kratos::config::BoxConfig & {
    return config_;
  }
  virtual auto get_logger_appender() -> klogger::Appender* override {
    return nullptr;
  }

  /**
   * 获取本地化实例.
   *
   * \return 本地化实例
   */
  virtual auto get_lang() -> kratos::lang::Lang * override { return nullptr; }
  virtual void on_listen(const std::string &name, bool success,
                         std::shared_ptr<kratos::service::BoxChannel>& channel) {
    listened = success;
  }
  virtual void on_accept(std::shared_ptr<kratos::service::BoxChannel>& channel) {
    accepted = true;
    client = channel;
  }
  virtual void
  on_connect(const std::string &name, bool success,
             std::shared_ptr<kratos::service::BoxChannel>& channel) {
    connected = success;
    connector = channel;
  }
  virtual void on_close(std::shared_ptr<kratos::service::BoxChannel>& channel) {
    closed = true;
  }
  virtual void on_data(std::shared_ptr<kratos::service::BoxChannel>& channel) {
    recved = true;
  }
};

void net_update(kratos::service::BoxNetwork &net, int second) {
  auto start = kratos::util::get_os_time_millionsecond();
  second *= 1000;
  while (true) {
    net.update();
    auto now = kratos::util::get_os_time_millionsecond();
    if (now - start > (std::time_t)second) {
      break;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }
}

CASE(TestListen1) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(!net.listen_at("", "", 0));
  ASSERT_TRUE(net.listen_at("test", "127.0.0.1", 2001));
  ASSERT_TRUE(!net.listened);
  net_update(net, 1);
  ASSERT_TRUE(net.listened);
}

CASE(TestListen2) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.listen_at("test", "1.2.3.4", 2001));
  ASSERT_TRUE(!net.listened);
  net_update(net, 1);
  ASSERT_TRUE(!net.listened);
}

CASE(TestConnect1) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.connect_to("test", "4.5.6.7", 2001, 1));
  ASSERT_TRUE(!net.connected);
  net_update(net, 1);
  ASSERT_TRUE(!net.connected);
}

CASE(TestConnect2) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.listen_at("test", "127.0.0.1", 2001));
  ASSERT_TRUE(net.connect_to("test1", "127.0.0.1", 2001, 1));
  net_update(net, 1);
  ASSERT_TRUE(net.listened);
  ASSERT_TRUE(net.connected);
}

CASE(TestConnect3) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.listen_at("test", "127.0.0.1", 2001));
  ASSERT_TRUE(net.connect_to("test1", "127.0.0.1", 2001, 1));
  net_update(net, 1);
  ASSERT_TRUE(net.listened);
  ASSERT_TRUE(net.connected);
  ASSERT_TRUE(net.client->get_id() != 0);
  net.client->close();
  net_update(net, 1);
}

CASE(TestConnect4) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(!net.connect_to("", "127.0.0.1", 2001, 1));
  ASSERT_TRUE(!net.connect_to("test", "ssss", 2001, 1));
  ASSERT_TRUE(!net.connect_to("test", "1.1.1.1", 0, 1));
}

CASE(TestSend1) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.listen_at("test", "127.0.0.1", 2001));
  ASSERT_TRUE(net.connect_to("test1", "127.0.0.1", 2001, 1));
  net_update(net, 1);
  ASSERT_TRUE(net.listened);
  ASSERT_TRUE(net.connected);
  auto id = net.client->get_id();
  ASSERT_TRUE(net.get_channel(id) == net.client);
  net.connector->send("abc", 3);
  net_update(net, 1);
  ASSERT_TRUE(net.recved);
}

CASE(TestSend2) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.listen_at("test", "127.0.0.1", 2001));
  ASSERT_TRUE(net.connect_to("test1", "127.0.0.1", 2001, 1));
  net_update(net, 1);
  ASSERT_TRUE(net.listened);
  ASSERT_TRUE(net.connected);
  net.connector->send("abc", 3);
  net_update(net, 1);
  ASSERT_TRUE(net.recved);
  ASSERT_TRUE(3 == net.client->skip(3));
  ASSERT_TRUE(0 == net.client->skip(0));
  net.client->close();
  ASSERT_TRUE(0 == net.client->skip(3));
}

CASE(TestSend3) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.listen_at("test", "127.0.0.1", 2001));
  ASSERT_TRUE(net.connect_to("test1", "127.0.0.1", 2001, 1));
  net_update(net, 1);
  ASSERT_TRUE(net.listened);
  ASSERT_TRUE(net.connected);
  ASSERT_TRUE(0 == net.client->skip(10));
  net.client->send("abc", 3);
  ASSERT_TRUE(!net.client->send(nullptr, 0));
  net_update(net, 1);
  char buffer[64] = {0};
  ASSERT_TRUE(3 == net.connector->size());
  ASSERT_TRUE(0 == net.connector->peek(nullptr, 0));
  ASSERT_TRUE(3 == net.connector->peek(buffer, sizeof(buffer)));
  ASSERT_TRUE(net.recved);
  ASSERT_TRUE(3 == net.connector->recv(buffer, sizeof(buffer)));
  ASSERT_TRUE(0 == net.connector->size());
  ASSERT_TRUE(0 == net.connector->recv(nullptr, 0));
  auto id = net.client->get_id();
  net.client->close();
  ASSERT_TRUE(1 == net.client->write_buffer("1", 1));
  ASSERT_TRUE(net.client->isClose());
  // 多次关闭
  net.client->close();
  net_update(net, 1);
  ASSERT_TRUE(nullptr == net.get_channel(id));
  ASSERT_TRUE(!net.client->send("abc", 3));
  ASSERT_TRUE(0 == net.connector->recv(buffer, sizeof(buffer)));
  ASSERT_TRUE(0 == net.connector->peek(buffer, sizeof(buffer)));
  ASSERT_TRUE(0 == net.connector->size());
  ASSERT_TRUE("test1" == net.connector->get_channel_name());
}

CASE(TestSend4) {
  TestNetwork net;
  ASSERT_TRUE(net.start());
  ASSERT_TRUE(net.listen_at("test", "127.0.0.1", 2001));
  ASSERT_TRUE(net.connect_to("test1", "127.0.0.1", 2001, 1));
  net_update(net, 1);
  ASSERT_TRUE(net.listened);
  ASSERT_TRUE(net.connected);
  auto id = net.client->get_id();
  ASSERT_TRUE(net.get_channel(id) == net.client);
  std::unique_ptr<char> data(new char[1024 * 32]);
  ASSERT_TRUE(1024 * 32 == net.connector->send(data.get(), 1024 * 32));
  net_update(net, 1);
  ASSERT_TRUE(net.recved);
}

FIXTURE_END(test_box_network)
